package com.avalieme.classificadores;

import java.io.File;
import java.io.Serializable;

import com.avalieme.util.Constantes;

import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.StringToWordVector;

/**
 * Classe responsável por manipular o classificador
 * 
 * @author Jose Laerte
 * @author Raissa Sarmento
 *
 */
public class Classificador implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 3181181955184212322L;


	private static final int QTD_CLASSES = 2;
	private static final int QTD_ATRIBUTOS = 2;
	
	private static final String NOME_CONJUNTO_TREINAMENTO = "TreinamentoClassificadorTweets";
	private static final String NOME_CONJUNTO_TESTES = "TestesClassificadorTweets";
	
	private static final int CAPACIDADE = 1000000;

	private static final String CLASSE = "Class";
	private static final String TWEET = "Tweet";

	private Instances dadosTeste;
	private Instances dadosTreinamento;
	private StringToWordVector filtro;
	private boolean atualizacao;
	private String localModelo;
	private Classifier classificador;
	
	public Classificador(Classifier classificador, String localModelo, Boolean positivoNegativo) {
		this.localModelo = localModelo;
		this.filtro = carregarFiltro();
		this.classificador = classificador;
		dadosTreinamento = criarInstancias(NOME_CONJUNTO_TREINAMENTO, positivoNegativo);
		dadosTeste = criarInstancias(NOME_CONJUNTO_TESTES, positivoNegativo);
	}
	
	public String getLocalModelo() {
		return localModelo;
	}

	public String getNomeClassificador() {
		return classificador.getClass().getName();
	}

	public void treinar(String tweet, String classificacao) {
		Instance instancia = createInstance(tweet, dadosTreinamento);
		instancia.setClassValue(classificacao);
		dadosTreinamento.add(instancia);
		atualizacao = false;
	}

	public void testar(String tweet, String classificacao) {
		Instance instance = createInstance(tweet, dadosTeste);
		instance.setClassValue(classificacao);
		dadosTeste.add(instance);
	}

	public Evaluation evaluate() throws ClassificadorException {
		try {
			carregar();

			Instances filteredData = Filter.useFilter(dadosTeste, filtro);

			Evaluation evaluation = new Evaluation(filteredData);
			evaluation.evaluateModel(classificador, filteredData);
			return evaluation;
		} catch (Exception e) {
			e.printStackTrace();
			throw new ClassificadorException(e.getMessage());
		}
	}

	public String classificar(String tweet) throws ClassificadorException {
		if (dadosTreinamento.numInstances() == 0) {
			throw new ClassificadorException("O Classificador não foi treinado. Tente novamente!");
		}
		carregar();
		Instances testset = dadosTreinamento.stringFreeStructure();
		Instance instance = createInstance(tweet, testset);
		return classificar(instance);
	}

	private String classificar(Instance instance) throws ClassificadorException {
		try {
			filtro.input(instance);
			double predValue = classificador.classifyInstance(filtro.output());
			return dadosTreinamento.classAttribute().value((int) predValue);
		} catch (Exception e) {
			e.printStackTrace();
			throw new ClassificadorException(e.getMessage());
		}
	}
	
	private StringToWordVector carregarFiltro() {
		StringToWordVector filter = new StringToWordVector();
		filter.setTokenizer(new TratamentoTweets());
		filter.setStopwords(new File(Constantes.STOPWORDS));
		filter.setLowerCaseTokens(true);
		return filter;
	}

	private Instance createInstance(String text, Instances data) {
		Instance instance = new Instance(QTD_CLASSES);
		Attribute messageAtt = data.attribute(TWEET);
		instance.setValue(messageAtt, messageAtt.addStringValue(text));
		instance.setDataset(data);
		return instance;
	}

	private Instances criarInstancias(String name, Boolean positivoNegativo) {
		FastVector classes = new FastVector(QTD_ATRIBUTOS);
		
		if(positivoNegativo) {
			classes.addElement("positivo");
			classes.addElement("negativo");
		} else {
			classes.addElement("opinativo");
			classes.addElement("neutro");
		}
		FastVector attributes = new FastVector(QTD_CLASSES);
		attributes.addElement(new Attribute(TWEET, (FastVector) null));
		attributes.addElement(new Attribute(CLASSE, classes));
		Instances instances = new Instances(name, attributes, CAPACIDADE);
		instances.setClassIndex(instances.numAttributes()-1);
		return instances;
	}
	
	public void carregar() throws ClassificadorException {
		if (!atualizacao) {
			try {
				filtro.setInputFormat(dadosTreinamento);
				Instances filteredData = Filter.useFilter(dadosTreinamento, filtro);
				classificador.buildClassifier(filteredData);
			} catch (Exception e) {
				throw new ClassificadorException(e.getMessage());
			}
			atualizacao = true;
		}
	}
	

	public String toString() {
		return classificador.toString();
	}
}